// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package trace

import (
	"container/heap"
	"math"
	"sort"
	"strings"
	"time"
)

// MutatorUtil是在特定的
// 时间对mutator利用率的改变。Mutator使用函数表示为
// 时间顺序[]MutatorUtil。
type MutatorUtil struct {
	Time int64
	// Util是从时间开始的平均变异子利用率。此
	// 在范围[0,1]内。
	Util float64
}

// UtilFlags控制MutatorUtilization的行为。
type UtilFlags int

const (
	// UtilSTW意味着利用率应考虑STW事件。
	UtilSTW UtilFlags = 1 << iota
	// UtilBackground意味着利用率应考虑
	// 背景标记工人。
	UtilBackground
	// Utilasist表示利用率应计入标记
	// 助攻。
	UtilAssist
	// UtilSweep意味着利用率应考虑到清扫。
	UtilSweep

	// UtilPerProc意味着每个P都应该有一个单独的
	// 利用函数。否则，只有一个函数
	// 并且每个P都得到了利用率的一小部分。
	UtilPerProc
)

// MutatorUtilization返回给定跟踪的一组mutator使用函数
// 。每个函数将始终以0 
// 使用率结束。每个函数的边界都隐含在第一个
// 和最后一个事件中；在这些界限之外，每个函数都是未定义的。
// 
// 如果未给出UtilPerProc标志，则始终返回单个
// 使用函数。否则，它将为每个P返回一个函数。
func MutatorUtilization(events []*Event, flags UtilFlags) [][]MutatorUtil {
	if len(events) == 0 {
		return nil
	}

	type perP struct {
		// gc>0表示gc在此P上处于活动状态。
		gc int
		// 系列此P的逻辑序列号。此
		// 是必需的，因为可能会删除Ps，然后重新添加
		// 然后新P需要一个新系列。
		series int
	}
	ps := []perP{}
	stw := 0

	out := [][]MutatorUtil{}
	assists := map[uint64]bool{}
	block := map[uint64]*Event{}
	bgMark := map[uint64]bool{}

	for _, ev := range events {
		switch ev.Type {
		case EvGomaxprocs:
			gomaxprocs := int(ev.Args[0])
			if len(ps) > gomaxprocs {
				if flags&UtilPerProc != 0 {
					// 结束每个P的系列。
					for _, p := range ps[gomaxprocs:] {
						out[p.series] = addUtil(out[p.series], MutatorUtil{ev.Ts, 0})
					}
				}
				ps = ps[:gomaxprocs]
			}
			for len(ps) < gomaxprocs {
				// 开始新的P系列。
				series := 0
				if flags&UtilPerProc != 0 || len(out) == 0 {
					series = len(out)
					out = append(out, []MutatorUtil{{ev.Ts, 1}})
				}
				ps = append(ps, perP{series: series})
			}
		case EvGCSTWStart:
			if flags&UtilSTW != 0 {
				stw++
			}
		case EvGCSTWDone:
			if flags&UtilSTW != 0 {
				stw--
			}
		case EvGCMarkAssistStart:
			if flags&UtilAssist != 0 {
				ps[ev.P].gc++
				assists[ev.G] = true
			}
		case EvGCMarkAssistDone:
			if flags&UtilAssist != 0 {
				ps[ev.P].gc--
				delete(assists, ev.G)
			}
		case EvGCSweepStart:
			if flags&UtilSweep != 0 {
				ps[ev.P].gc++
			}
		case EvGCSweepDone:
			if flags&UtilSweep != 0 {
				ps[ev.P].gc--
			}
		case EvGoStartLabel:
			if flags&UtilBackground != 0 && strings.HasPrefix(ev.SArgs[0], "GC ") && ev.SArgs[0] != "GC (idle)" {
				// 背景标记工人。
				// 
				// 如果我们处于每进程模式，我们不计算专门的工作人员，因为
				// 他们将所有的goroutine从
				// 那P中踢出，所以不要直接导致goroutine延迟。
				if !(flags&UtilPerProc != 0 && ev.SArgs[0] == "GC (dedicated)") {
					bgMark[ev.G] = true
					ps[ev.P].gc++
				}
			}
			fallthrough
		case EvGoStart:
			if assists[ev.G] {
				// 在协助期间未被阻止。
				ps[ev.P].gc++
			}
			block[ev.G] = ev.Link
		default:
			if ev != block[ev.G] {
				continue
			}

			if assists[ev.G] {
				// 在辅助过程中被阻塞。
				ps[ev.P].gc--
			}
			if bgMark[ev.G] {
				// 背景标记工作已完成。
				ps[ev.P].gc--
				delete(bgMark, ev.G)
			}
			delete(block, ev.G)
		}

		if flags&UtilPerProc == 0 {
			// 计算当前平均利用率。
			if len(ps) == 0 {
				continue
			}
			gcPs := 0
			if stw > 0 {
				gcPs = len(ps)
			} else {
				for i := range ps {
					if ps[i].gc > 0 {
						gcPs++
					}
				}
			}
			mu := MutatorUtil{ev.Ts, 1 - float64(gcPs)/float64(len(ps))}

			// 记录使用率的变化。（由于
			// len（ps）=len（out），我们知道len（out）>0。）
			out[0] = addUtil(out[0], mu)
		} else {
			// 检查per-P利用率的变化。
			for i := range ps {
				p := &ps[i]
				util := 1.0
				if stw > 0 || p.gc > 0 {
					util = 0.0
				}
				out[p.series] = addUtil(out[p.series], MutatorUtil{ev.Ts, util})
			}
		}
	}

	// 将最终0利用率事件添加到任何剩余系列中。此
	// 对于标记跟踪结束非常重要。确切值
	// 应该无关紧要，因为任何窗口都不应超出此范围，但是使用0与跟踪的开始对称。
	mu := MutatorUtil{events[len(events)-1].Ts, 0}
	for i := range ps {
		out[ps[i].series] = addUtil(out[ps[i].series], mu)
	}
	return out
}

func addUtil(util []MutatorUtil, mu MutatorUtil) []MutatorUtil {
	if len(util) > 0 {
		if mu.Util == util[len(util)-1].Util {
			// 无变化。
			return util
		}
		if mu.Time == util[len(util)-1].Time {
			// 在一个时间戳中使用最低的利用率。
			if mu.Util < util[len(util)-1].Util {
				util[len(util)-1] = mu
			}
			return util
		}
	}
	return append(util, mu)
}

// totalUtil是总利用率，以纳秒为单位。这是一个
// 单独的类型，主要是为了将其与平均利用率区分开来，
// 也是一个浮点64。
type totalUtil float64

func totalUtilOf(meanUtil float64, dur int64) totalUtil {
	return totalUtil(meanUtil * float64(dur))
}

// mean返回dur的平均利用率。
func (u totalUtil) mean(dur time.Duration) float64 {
	return float64(u) / float64(dur)
}

// 一条MMUCurve是跨越多个窗口大小的最小变异子利用率曲线。
type MMUCurve struct {
	series []mmuSeries
}

type mmuSeries struct {
	util []MutatorUtil
	// sums[j]是util[：j]的累积和。
	sums []totalUtil
	// 频带在持续时间不重叠的频带中汇总UTI 
	// bandDur。
	bands []mmuBand
	// bandDur是每个波段的持续时间。
	bandDur int64
}

type mmuBand struct {
	// minutel是
	// 此频带中的最小瞬时突变子利用率。
	minUtil float64
	// cumultil是
	// 时间0和该频带左边缘之间的累积总突变子利用率。
	cumUtil totalUtil

	// 积分器是此
	// 频带左边缘的积分器。
	integrator integrator
}

// NewMMUCurve返回给定突变子利用率的MMU曲线
// 函数。
func NewMMUCurve(utils [][]MutatorUtil) *MMUCurve {
	series := make([]mmuSeries, len(utils))
	for i, util := range utils {
		series[i] = newMMUSeries(util)
	}
	return &MMUCurve{series}
}

// bandsPerSeries是将每个系列划分为的波段数。
// 这仅通过测试更改。
var bandsPerSeries = 1000

func newMMUSeries(util []MutatorUtil) mmuSeries {
	// 计算累计和。
	sums := make([]totalUtil, len(util))
	var prev MutatorUtil
	var sum totalUtil
	for j, u := range util {
		sum += totalUtilOf(prev.Util, u.Time-prev.Time)
		sums[j] = sum
		prev = u
	}

	// 将利用率曲线分成大小相等的
	// 非重叠的“带”，并计算每个带的汇总。
	// 
	// 计算每个频带的持续时间。
	numBands := bandsPerSeries
	if numBands > len(util) {
		// 如果没有太多的活动，那么有很多乐队是没有意义的。
		numBands = len(util)
	}
	dur := util[len(util)-1].Time - util[0].Time
	bandDur := (dur + int64(numBands) - 1) / int64(numBands)
	if bandDur < 1 {
		bandDur = 1
	}
	// 计算频带。有numBands+1条带用于
	// 记录最终累计金额。
	bands := make([]mmuBand, numBands+1)
	s := mmuSeries{util, sums, bands, bandDur}
	leftSum := integrator{&s, 0}
	for i := range bands {
		startTime, endTime := s.bandTime(i)
		cumUtil := leftSum.advance(startTime)
		predIdx := leftSum.pos
		minUtil := 1.0
		for i := predIdx; i < len(util) && util[i].Time < endTime; i++ {
			minUtil = math.Min(minUtil, util[i].Util)
		}
		bands[i] = mmuBand{minUtil, cumUtil, leftSum}
	}

	return s
}

func (s *mmuSeries) bandTime(i int) (start, end int64) {
	start = int64(i)*s.bandDur + s.util[0].Time
	end = start + s.bandDur
	return
}

type bandUtil struct {
	// 利用率系列索引
	series int
	// 带索引
	i int
	// 所有窗口的变异子利用率下限
	// 带左边缘。
	utilBound float64
}

type bandUtilHeap []bandUtil

func (h bandUtilHeap) Len() int {
	return len(h)
}

func (h bandUtilHeap) Less(i, j int) bool {
	return h[i].utilBound < h[j].utilBound
}

func (h bandUtilHeap) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

func (h *bandUtilHeap) Push(x interface{}) {
	*h = append(*h, x.(bandUtil))
}

func (h *bandUtilHeap) Pop() interface{} {
	x := (*h)[len(*h)-1]
	*h = (*h)[:len(*h)-1]
	return x
}

// UtilWindow是当时的一个特定窗口。
type UtilWindow struct {
	Time int64
	// MutatorUtil是此窗口中的平均mutator使用率。
	MutatorUtil float64
}

type utilHeap []UtilWindow

func (h utilHeap) Len() int {
	return len(h)
}

func (h utilHeap) Less(i, j int) bool {
	if h[i].MutatorUtil != h[j].MutatorUtil {
		return h[i].MutatorUtil > h[j].MutatorUtil
	}
	return h[i].Time > h[j].Time
}

func (h utilHeap) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

func (h *utilHeap) Push(x interface{}) {
	*h = append(*h, x.(UtilWindow))
}

func (h *utilHeap) Pop() interface{} {
	x := (*h)[len(*h)-1]
	*h = (*h)[:len(*h)-1]
	return x
}

// 累加器采用带窗口的mutator use函数，
// 跟踪该函数的各种统计信息。
type accumulator struct {
	mmu float64

	// bound是突变子利用率边界，在此边界之上添加任何
	// 突变子利用率都不会影响
	// 累积统计数据。
	bound float64

	// 最差N窗口跟踪
	nWorst int
	wHeap  utilHeap

	// 突变子利用率分布跟踪
	mud *mud
	// preciseMass是在停止累积之前必须精确的分布质量
	// 。
	preciseMass float64
	// lastTime和lastMU是添加到
	// 带窗口的mutator use函数的上一个点。
	lastTime int64
	lastMU   float64
}

// resetTime通过重置当前时间在加窗突变子
// 利用函数中声明不连续性。
func (acc *accumulator) resetTime() {
	// 这只对分发收集重要，因为这是
	// 唯一取决于
	// 加窗突变子利用函数的进程的东西。
	acc.lastTime = math.MaxInt64
}

// addMU在
// （time，mu）处向加窗的mutator use函数添加一个点。对于时间的单调递增值
// 必须调用此函数。
// 
// 如果进一步调用addMU没有意义，则返回true。
func (acc *accumulator) addMU(time int64, mu float64, window time.Duration) bool {
	if mu < acc.mmu {
		acc.mmu = mu
	}
	acc.bound = acc.mmu

	if acc.nWorst == 0 {
		// 如果最小值达到零，它就不能再低了，所以我们可以早点停下来。ABCDEGG 
		return mu == 0
	}

	if len(acc.wHeap) < acc.nWorst || mu < acc.wHeap[0].MutatorUtil {
		// 此窗口低于第K个最差窗口。
		// 
		// 检查堆中是否存在重叠窗口
		// 并保留更差的窗口。
		for i, ui := range acc.wHeap {
			if time+int64(window) > ui.Time && ui.Time+int64(window) > time {
				if ui.MutatorUtil <= mu {
					// 保留第一个窗口。
					goto keep
				} else {
					// 用此窗口替换它。
					heap.Remove(&acc.wHeap, i)
					break
				}
			}
		}

		heap.Push(&acc.wHeap, UtilWindow{time, mu})
		if len(acc.wHeap) > acc.nWorst {
			heap.Pop(&acc.wHeap)
		}
	keep:
	}

	if len(acc.wHeap) < acc.nWorst {
		// 我们还没有N个窗口，所以继续积累。
		acc.bound = 1.0
	} else {
		// 任何高于最差窗口的内容都无效。
		acc.bound = math.Max(acc.bound, acc.wHeap[0].MutatorUtil)
	}

	if acc.mud != nil {
		if acc.lastTime != math.MaxInt64 {
			// 更新分发。
			acc.mud.add(acc.lastMU, mu, float64(time-acc.lastTime))
		}
		acc.lastTime, acc.lastMU = time, mu
		if _, mudBound, ok := acc.mud.approxInvCumulativeSum(); ok {
			acc.bound = math.Max(acc.bound, mudBound)
		} else {
			// 我们还没有积累足够的总精确
			// 质量甚至还没有达到我们的目标，所以继续积累。
			acc.bound = 1
		}
		// 不值得每次都检查百分位数，所以
		// 只需不断累积这个范围。
		return false
	}

	// 如果我们发现足够的0次使用，我们可以立即停止。
	return len(acc.wHeap) == acc.nWorst && acc.wHeap[0].MutatorUtil == 0
}

// MMU返回给定时间内的最小变异子利用率
// 窗口。这是
// 整个执行期间所有窗口的最低利用率。返回值的范围为
// /[0,1]。
func (c *MMUCurve) MMU(window time.Duration) (mmu float64) {
	acc := accumulator{mmu: 1.0, bound: 1.0}
	c.mmu(window, &acc)
	return acc.mmu
}

// Examples返回给定窗口大小的最低mutator的n个特定示例
// 利用率。返回的窗口将是
// 不相交的（否则将有大量
// 大多数在单个最低点重叠的窗口）。
// 不保证返回哪一组不相交的窗口。
func (c *MMUCurve) Examples(window time.Duration, n int) (worst []UtilWindow) {
	acc := accumulator{mmu: 1.0, bound: 1.0, nWorst: n}
	c.mmu(window, &acc)
	sort.Sort(sort.Reverse(acc.wHeap))
	return ([]UtilWindow)(acc.wHeap)
}

// MUD返回给定窗口大小的
// 的突变子利用率分布分位数。
// 
// 突变子利用率分布是平均
// 突变子利用率在
// 跟踪中给定窗口大小的所有窗口中的分布。
// 
// 最小突变子利用率是该分布的最小值（第0百分位）。（但是，如果只需要最小值，则使用MMU方法更有效。）
func (c *MMUCurve) MUD(window time.Duration, quantiles []float64) []float64 {
	if len(quantiles) == 0 {
		return []float64{}
	}

	// 每个未定义的带对
	// 分布贡献了一个已知的总质量（除末端外的带度），但以未知的方式。然而，我们知道它所贡献的所有质量必须达到或高于其最坏情况下的平均突变子利用率。
	// 
	// 因此，我们细化波段，直到最高期望
	// 分布分位数小于下一个最坏情况下的平均
	// 突变子利用率。此时，所有进一步的
	// 对分布的贡献必须超过
	// 所需的分位数，因此不能影响它。
	// 
	// 首先，找到所需的最高分布分位数。
	maxQ := quantiles[0]
	for _, q := range quantiles {
		if q > maxQ {
			maxQ = q
		}
	}
	// 分布的质量是以时间为单位的（它不是
	// 标准化的，因为这会使
	// 说明未定义带的未来贡献）。
	// 最终总质量将是跟踪本身的持续时间
	// 减去窗口大小。利用这个，我们可以计算对应于分位数maxQ的质量
	// 。
	var duration int64
	for _, s := range c.series {
		duration1 := s.util[len(s.util)-1].Time - s.util[0].Time
		if duration1 >= int64(window) {
			duration += duration1 - int64(window)
		}
	}
	qMass := float64(duration) * maxQ

	// 积累泥浆，直到我们获得关于
	// qMass左侧的所有信息。
	acc := accumulator{mmu: 1.0, bound: 1.0, preciseMass: qMass, mud: new(mud)}
	acc.mud.setTrackMass(qMass)
	c.mmu(window, &acc)

	// 评估累积泥浆的分位数。有几种合法的方法可以做到这一点：
	out := make([]float64, len(quantiles))
	for i := range out {
		mu, _ := acc.mud.invCumulativeSum(float64(duration) * quantiles[i])
		if math.IsNaN(mu) {
			// 发生：
			// 
			// 1。如果窗口是完整跟踪
			// 持续时间，那么加窗的MU函数是
			// 仅在一个点上定义，因此MU 
			// 分布没有很好的定义。中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网。如果没有事件，则MU 
			// 分布没有质量。
			// 
			// 无论哪种方式，此时所有分位数都将向MMU收敛。
			mu = acc.mmu
		}
		out[i] = mu
	}
	return out
}

func (c *MMUCurve) mmu(window time.Duration, acc *accumulator) {
	if window <= 0 {
		acc.mmu = 0
		return
	}

	var bandU bandUtilHeap
	windows := make([]time.Duration, len(c.series))
	for i, s := range c.series {
		windows[i] = window
		if max := time.Duration(s.util[len(s.util)-1].Time - s.util[0].Time); window > max {
			windows[i] = max
		}

		bandU1 := bandUtilHeap(s.mkBandUtil(i, windows[i]))
		if bandU == nil {
			bandU = bandU1
		} else {
			bandU = append(bandU, bandU1...)
		}
	}

	// 处理从最低利用率绑定到最高利用率的频带。
	heap.Init(&bandU)

	// 将每个频带细化为精确的窗口和MMU，直到
	// 细化下一个最低频带不再影响MMU 
	// 或窗口。
	for len(bandU) > 0 && bandU[0].utilBound < acc.bound {
		i := bandU[0].series
		c.series[i].bandMMU(bandU[0].i, windows[i], acc)
		heap.Pop(&bandU)
	}
}

func (c *mmuSeries) mkBandUtil(series int, window time.Duration) []bandUtil {
	// 对于每个波段，计算在该波段中启动的所有窗口的最差可能总突变因子
	// 利用率。

	// minBands是窗口可以跨越的最小频带数
	// ，maxBands是窗口可以跨越的任何对齐的最大频带数
	// 。
	minBands := int((int64(window) + c.bandDur - 1) / c.bandDur)
	maxBands := int((int64(window) + 2*(c.bandDur-1)) / c.bandDur)
	if window > 1 && maxBands < 2 {
		panic("maxBands < 2")
	}
	tailDur := int64(window) % c.bandDur
	nUtil := len(c.bands) - maxBands + 1
	if nUtil < 0 {
		nUtil = 0
	}
	bandU := make([]bandUtil, nUtil)
	for i := range bandU {
		// 为了计算最坏情况下的MU，我们假设对于仅部分被
		// 以及所有窗口完全覆盖的任何频带的平均值。
		// 某些窗口覆盖的任何频带，最小
		var util totalUtil

		// 查找部分
		// 条带中的最低和第二最低条带。
		l := c.bands[i].minUtil
		r1 := c.bands[i+minBands-1].minUtil
		r2 := c.bands[i+maxBands-1].minUtil
		minBand := math.Min(l, math.Min(r1, r2))
		// 假设最差窗口与
		// 最差最小值最大重叠，然后其余窗口与第二个
		// 最差最小值重叠。
		if minBands == 1 {
			util += totalUtilOf(minBand, int64(window))
		} else {
			util += totalUtilOf(minBand, c.bandDur)
			midBand := 0.0
			switch {
			case minBand == l:
				midBand = math.Min(r1, r2)
			case minBand == r1:
				midBand = math.Min(l, r2)
			case minBand == r2:
				midBand = math.Min(l, r1)
			}
			util += totalUtilOf(midBand, tailDur)
		}

		// 加上所有窗口完全重叠的波段的总平均MU。
		if minBands > 2 {
			util += c.bands[i+minBands-1].cumUtil - c.bands[i+1].cumUtil
		}

		bandU[i] = bandUtil{series, i, util.mean(window)}
	}

	return bandU
}

// bandMMU计算带内带左边缘bandIdx的
// 窗口的精确最小变异子利用率。
func (c *mmuSeries) bandMMU(bandIdx int, window time.Duration, acc *accumulator) {
	util := c.util

	// 我们将随时间变化的突变子利用率视为
	// 框过滤利用率函数，我们称之为
	// “窗口化突变子利用率函数”。得到的
	// 函数是连续的和分段线性的（除非
	// window==0，我们在别处处理），其中，当窗口
	// 出现。因此，这个函数的最小值abcDef
	// 的任一边缘遇到瞬时突变子
	// 利用函数的变化时，段之间的边界
	// 
	// 我们通过跟踪从t=0到
	// 窗口左边缘和窗口右边缘的积分，以增量方式计算突变子利用函数。
	left := c.bands[bandIdx].integrator
	right := left
	time, endTime := c.bandTime(bandIdx)
	if utilEnd := util[len(util)-1].Time - int64(window); utilEnd < endTime {
		endTime = utilEnd
	}
	acc.resetTime()
	for {
		// 将边缘提前到时间和时间+窗口。
		mu := (right.advance(time+int64(window)) - left.advance(time)).mean(window)
		if acc.addMU(time, mu, window) {
			break
		}
		if time == endTime {
			break
		}

		// 加窗突变子
		// 利用函数的最大斜率为1/window，因此我们始终可以将时间至少提前（mu-mmu）*window 
		// 而不降低到mmu以下。
		minTime := time + int64((mu-acc.bound)*float64(window))

		// 将窗口提前到下一次，此时窗口的左边缘或右边缘遇到
		// 利用率曲线的变化。
		if t1, t2 := left.next(time), right.next(time+int64(window))-int64(window); t1 < t2 {
			time = t1
		} else {
			time = t2
		}
		if time < minTime {
			time = minTime
		}
		if time >= endTime {
			// 对于MMU，我们可以到此为止，但是对于MUD，我们必须跨越整个
			// 频带。
			time = endTime
		}
	}
}

// 积分器跟踪利用率函数中的位置，
// 对其进行集成。
type integrator struct {
	u *mmuSeries
	// pos是当前时间的非严格的
	// 前辈的u.util索引。
	pos int
}

// advance返回利用率函数从0到
// 时间的整数。必须对
// /次的单调递增值调用advance。
func (in *integrator) advance(time int64) totalUtil {
	util, pos := in.u.util, in.pos
	// 提前pos，直到pos+1成为time的严格继承者（使
	// pos time的非严格继承者）。
	// 
	// 通常情况下，这将在附近，因此我们优化了该情况，
	// 但它可能任意远离，因此我们也有效地处理了该
	// 问题。
	const maxSeq = 8
	if pos+maxSeq < len(util) && util[pos+maxSeq].Time > time {
		// 附近。使用线性扫描。
		for pos+1 < len(util) && util[pos+1].Time <= time {
			pos++
		}
	} else {
		// 远。二进制搜索时间的严格继承者。
		l, r := pos, len(util)
		for l < r {
			h := int(uint(l+r) >> 1)
			if util[h].Time <= time {
				l = h + 1
			} else {
				r = h
			}
		}
		pos = l - 1 // 非严格前置。
	}
	in.pos = pos
	var partial totalUtil
	if time != util[pos].Time {
		partial = totalUtilOf(util[pos].Util, time-util[pos].Time)
	}
	return in.u.sums[pos] + partial
}

// 下一步返回
// 使用函数中更改的最小时间t'>time。
func (in *integrator) next(time int64) int64 {
	for _, u := range in.u.util[in.pos:] {
		if u.Time > time {
			return u.Time
		}
	}
	return 1<<63 - 1
}
